﻿#include "xcustompointlines_p.h"

/*!
 * \brief XCustomPointLinePrivate::XCustomPointLinePrivate
 */
XCustomPointLinePrivate::XCustomPointLinePrivate()
    : mPenColor(QColor(20, 20, 20)),
      mBrushColor(QColor(20, 20, 20)),
      mTextPosition(None_ValueText),
      mTextOffset(5),
      mTextFont(QFont(QLatin1String("sans serif"), 8)),
      mTextColor(Qt::black)
{

}

/*!
 * \brief XCustomPointLinePrivate::~XCustomPointLinePrivate
 */
XCustomPointLinePrivate::~XCustomPointLinePrivate()
{

}

/*!
 * \brief XCustomPointLinePrivate::getDataRange
 * \param range
 * \param data
 */
void XCustomPointLinePrivate::getDataRange(QCPRange &range, QVector<double> &data)
{
    //获取数据的范围
    QVector<double>::iterator min = std::min_element(std::begin(data), std::end(data));
    QVector<double>::iterator max = std::max_element(std::begin(data), std::end(data));

    range.lower = (*min<0)?(1.2*(*min)):(0);
    range.upper = (*max>0)?(1.2*(*max)):(0);
}

/*!
 * \brief XCustomPointLine::XCustomPointLine
 * \param parent
 */
XCustomPointLine::XCustomPointLine(QCustomPlot *parent)
    : QCPGraph(parent->xAxis, parent->yAxis),
      XCustomPointLineP(new XCustomPointLinePrivate)
{

}

/*!
 * \brief XCustomPointLine::~XCustomPointLine
 */
XCustomPointLine::~XCustomPointLine()
{
}

/*!
 * \brief XCustomPointLine::getSeriesType
 * \return
 */
ChartSeriesType XCustomPointLine::getSeriesType() const
{
    return XCustomPointLineP->seriesType;
}

/*!
 * \brief XCustomPointLine::getScatterShape
 * \return
 */
QCPScatterStyle::ScatterShape XCustomPointLine::getScatterShape() const
{
    return XCustomPointLineP->scatterStyle;
}

/*!
 * \brief XCustomPointLine::getScatterSize
 * \return
 */
Integer XCustomPointLine::getScatterSize() const
{
    return XCustomPointLineP->scatterSize;
}

/*!
 * \brief XCustomPointLine::penColor
 * \return
 */
QColor XCustomPointLine::penColor() const
{
    return XCustomPointLineP->mPenColor;
}

/*!
 * \brief XCustomPointLine::brushColor
 * \return
 */
QColor XCustomPointLine::brushColor() const
{
    return XCustomPointLineP->mBrushColor;
}

/*!
 * \brief XCustomPointLine::textPosition
 * \return
 */
ChartValueTextPosition XCustomPointLine::textPosition() const
{
    return XCustomPointLineP->mTextPosition;
}

/*!
 * \brief XCustomPointLine::textOffset
 * \return
 */
double XCustomPointLine::textOffset() const
{
    return XCustomPointLineP->mTextOffset;
}

/*!
 * \brief XCustomPointLine::textFont
 * \return
 */
QFont XCustomPointLine::textFont() const
{
    return XCustomPointLineP->mTextFont;
}

/*!
 * \brief XCustomPointLine::textColor
 * \return
 */
QColor XCustomPointLine::textColor() const
{
    return XCustomPointLineP->mTextColor;
}

/*!
 * \brief XCustomPointLine::setValue
 * \param array
 */
void XCustomPointLine::setValue(Array &array)
{
    //先判断合法性
    //数组为空直接返回
    if(!array.Count())
        return;

    //判断数组类型
    QString typeName = array.ItemType();
    if ("Integer" != typeName || "Real" != typeName)
        return;

    //将Array转换为QVector
    XCustomPointLineP->data.clear();
    if("Integer" == typeName)
    {
        for(int i = 0; i < array.Count(); ++i)
        {
            XCustomPointLineP->data << array.At<Integer>(i).Value();
        }
    }
    else
    {
        for(int i = 0; i < array.Count(); ++i)
        {
            XCustomPointLineP->data << array.At<Real>(i).Value();
        }
    }

    //获取数据范围
    XCustomPointLineP->getDataRange(XCustomPointLineP->dataRange, XCustomPointLineP->data);

    //刷新主界面范围
    updateDataRange();

    //设置该pointLine数据
    this->setData(this->keyAxis()->tickVector(), XCustomPointLineP->data);
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setSeriesType
 * \param type
 */
void XCustomPointLine::setSeriesType(ChartSeriesType type)
{
    if(XCustomPointLineP->seriesType == type)
        return;

    XCustomPointLineP->seriesType = type;

    //根据数列类型设置对应属性
    switch(XCustomPointLineP->seriesType)
    {
    case Scatter_Series:
    {
        //散点图
        XCustomPointLineP->lineStyle = QCPGraph::lsNone;
        break;
    }
    case Impulse_Series:
    {
        //脉冲图
        XCustomPointLineP->lineStyle = QCPGraph::lsImpulse;
        break;
    }
    case Line_Series:
    {
        //折线图
        XCustomPointLineP->lineStyle = QCPGraph::lsLine;
        XCustomPointLineP->scatterStyle = QCPScatterStyle::ssNone;
        break;
    }
    case ScatterLine_Series:
    {
        //点线图
        XCustomPointLineP->lineStyle = QCPGraph::lsLine;
        break;
    }
    }

    this->setLineStyle(XCustomPointLineP->lineStyle);
    this->setScatterStyle(QCPScatterStyle(XCustomPointLineP->scatterStyle, XCustomPointLineP->scatterSize));
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setScatterShape
 * \param shape
 */
void XCustomPointLine::setScatterShape(QCPScatterStyle::ScatterShape shape)
{
    if(Line_Series == XCustomPointLineP->seriesType)
        return;

    if(XCustomPointLineP->scatterStyle == shape)
        return;

    XCustomPointLineP->scatterStyle = shape;

    this->setScatterStyle(QCPScatterStyle(XCustomPointLineP->scatterStyle, XCustomPointLineP->scatterSize));
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setScatterSize
 */
void XCustomPointLine::setScatterSize(Integer size)
{
    if(Line_Series == XCustomPointLineP->seriesType)
        return;

    if(XCustomPointLineP->scatterSize == size.Value())
        return;

    XCustomPointLineP->scatterSize = size.Value();

    this->setScatterStyle(QCPScatterStyle(XCustomPointLineP->scatterStyle, XCustomPointLineP->scatterSize));
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setPenColor
 * \param color
 */
void XCustomPointLine::setPenColor(const QColor &color)
{
    XCustomPointLineP->mPenColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setBrushColor
 * \param color
 */
void XCustomPointLine::setBrushColor(const QColor &color)
{
    XCustomPointLineP->mBrushColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setTextPosition
 * \param position
 */
void XCustomPointLine::setTextPosition(ChartValueTextPosition position)
{
    XCustomPointLineP->mTextPosition = position;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setTextOffset
 * \param spacing
 */
void XCustomPointLine::setTextOffset(double offset)
{
    XCustomPointLineP->mTextOffset = offset;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setTextFont
 * \param font
 */
void XCustomPointLine::setTextFont(const QFont &font)
{
    XCustomPointLineP->mTextFont = font;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::setTextColor
 * \param color
 */
void XCustomPointLine::setTextColor(const QColor &color)
{
    XCustomPointLineP->mTextColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomPointLine::updateDataRange
 */
void XCustomPointLine::updateDataRange()
{
    QCPRange parentRange = this->mParentPlot->yAxis->range();

    parentRange.lower = qMin(parentRange.lower, XCustomPointLineP->dataRange.lower);
    parentRange.upper = qMax(parentRange.upper, XCustomPointLineP->dataRange.upper);

    this->mParentPlot->yAxis->setRange(parentRange);
}

/* inherits documentation from base class */
void XCustomPointLine::draw(QCPPainter *painter)
{
    if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
    if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
    if (mLineStyle == lsNone && mScatterStyle.isNone()) return;

    XCustomPointLineP->lines.clear();
    XCustomPointLineP->scatters.clear();
    // loop over and draw segments of unselected/selected data:
    QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
    getDataSegments(selectedSegments, unselectedSegments);
    allSegments << unselectedSegments << selectedSegments;
    for (int i = 0; i < allSegments.size(); ++i)
    {
        bool isSelectedSegment = i >= unselectedSegments.size();
        // get line pixel points appropriate to line style:
        XCustomPointLineP->lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care)
        getLines(&XCustomPointLineP->lines, XCustomPointLineP->lineDataRange);

        qDebug() << "-----lines.size(): " << XCustomPointLineP->lines.size();

        // check data validity if flag set:
#ifdef QCUSTOMPLOT_CHECK_DATA
        QCPGraphDataContainer::const_iterator it;
        for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
        {
            if (QCP::isInvalidData(it->key, it->value))
                qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
        }
#endif
        qDebug() << "-----draw fill of graph:: ";
        // draw fill of graph:
        if (isSelectedSegment && mSelectionDecorator)
            mSelectionDecorator->applyBrush(painter);
        else
            painter->setBrush(mBrush);
        painter->setPen(Qt::NoPen);
        drawFill(painter, &XCustomPointLineP->lines);


        qDebug() << "-----draw line: ";
        // draw line:
        if (mLineStyle != lsNone)
        {
            if (isSelectedSegment && mSelectionDecorator)
                mSelectionDecorator->applyPen(painter);
            else
                painter->setPen(XCustomPointLineP->mPenColor);
            painter->setBrush(Qt::NoBrush);


            if (mLineStyle == lsImpulse)
                drawImpulsePlot(painter, XCustomPointLineP->lines);
            else
                drawLinePlot(painter, XCustomPointLineP->lines); // also step plots can be drawn as a line plot

        }

        qDebug() << "-----draw scatters: ";
        // draw scatters:
        QCPScatterStyle finalScatterStyle = mScatterStyle;
        if (isSelectedSegment && mSelectionDecorator)
            finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);
        if (!finalScatterStyle.isNone())
        {
            getScatters(&XCustomPointLineP->scatters, allSegments.at(i));
            drawScatterPlot(painter, XCustomPointLineP->scatters, finalScatterStyle);
        }

        // draw valueText
        drawValueText(painter);
    }

    // draw other selection decoration that isn't just line/scatter pens and brushes:
    if (mSelectionDecorator)
        mSelectionDecorator->drawDecoration(painter, selection());
}

/*!
 * \brief XCustomPointLine::drawValueText
 * \param painter
 */
void XCustomPointLine::drawValueText(QCPPainter *painter)
{
qDebug() << "-----drawValueText: ";
    // draw Text
    QCPGraphDataContainer::const_iterator visibleBegin, visibleEnd;
    getVisibleDataBounds(visibleBegin, visibleEnd, XCustomPointLineP->lineDataRange);

    if (visibleBegin == visibleEnd)
        return;

    //数据序号
    int order = 0;

    painter->setFont(XCustomPointLineP->mTextFont);  //设置字体
    painter->setPen(XCustomPointLineP->mTextColor);   //设置颜色
    for (QCPGraphDataContainer::const_iterator it = visibleBegin; it != visibleEnd; ++it)
    {
        // check data validity if flag set:
#ifdef QCUSTOMPLOT_CHECK_DATA
        if (QCP::isInvalidData(it->key, it->value))
            qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
#endif

        QString text = QString::number(it->value, 'g', 2);   // 取得当前value轴的值，保留两位精度
        QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignCenter, text);  // 计算文字所占用的大小

        // set the location of Text
        switch(XCustomPointLineP->mTextPosition)
        {
        case None_ValueText :
        {
            text.clear();
            textRect.setRect(0, 0, 0, 0);
            break;
        }
        case TopOutside_ValueText :
        case TopInside_ValueText :
        case CenterInside_ValueText :
        case BottomInside_ValueText :
        {
            if(lsNone == mLineStyle)
            {
                textRect.moveBottomLeft(XCustomPointLineP->scatters.at(order++) - QPointF(textRect.width() / 2, XCustomPointLineP->mTextOffset));
                break;
            }

            if(lsImpulse == mLineStyle)
            {
                textRect.moveBottomLeft(XCustomPointLineP->lines.at(++order) - QPointF(textRect.width() / 2, XCustomPointLineP->mTextOffset));
                ++order;
            }
            else
                textRect.moveBottomLeft(XCustomPointLineP->lines.at(order++) - QPointF(textRect.width() / 2, XCustomPointLineP->mTextOffset));

            break;
        }
        }
        painter->drawText(textRect, Qt::TextDontClip | Qt::AlignCenter, text); //draw
    }
}

/* inherits documentation from base class */
void XCustomPointLine::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
{
    applyScattersAntialiasingHint(painter);

    // set pen and brush
    painter->setPen(XCustomPointLineP->mPenColor);
    painter->setBrush(XCustomPointLineP->mBrushColor);

    // draw scatters in sequence
    foreach (const QPointF &scatter, scatters)
    {
        style.drawShape(painter, scatter.x(), scatter.y());
    }
}


/*!
 * \brief XCustomPointLinesPrivate::XCustomPointLinesPrivate
 */
XCustomPointLinesPrivate::XCustomPointLinesPrivate()
{

}

/*!
 * \brief XCustomPointLinesPrivate::~XCustomPointLinesPrivate
 */
XCustomPointLinesPrivate::~XCustomPointLinesPrivate()
{

}

/*!
 * \brief XCustomPointLines::XCustomPointLines
 * \param parent
 */
XCustomPointLines::XCustomPointLines(QCustomPlot *parent)
    : QObject(parent),
      pointLineList(new XCustomChartList<XCustomPointLine*>(parent)),
      customPointLinesParent(parent),
      XCustomPointLinesP(new XCustomPointLinesPrivate)
{
}

/*!
 * \brief XCustomPointLines::~XCustomPointLines
 */
XCustomPointLines::~XCustomPointLines()
{
    pointLineList = nullptr;
    customPointLinesParent = nullptr;
    XCustomPointLinesP = nullptr;
}



